home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / metamail / tahoe / collect.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-02-18  |  14.3 KB  |  796 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that this notice is preserved and that due credit is given
  7.  * to the University of California at Berkeley. The name of the University
  8.  * may not be used to endorse or promote products derived from this
  9.  * software without specific prior written permission. This software
  10.  * is provided ``as is'' without express or implied warranty.
  11.  */
  12.  
  13. #ifdef notdef
  14. static char sccsid[] = "@(#)collect.c    5.6 (Berkeley) 2/18/88";
  15. #endif /* notdef */
  16.  
  17. /*
  18.  * Mail -- a mail program
  19.  *
  20.  * Collect input from standard input, handling
  21.  * ~ escapes.
  22.  */
  23.  
  24. #include "rcv.h"
  25. #include <sys/stat.h>
  26. #include <sys/wait.h>
  27.  
  28. /*
  29.  * Read a message from standard output and return a read file to it
  30.  * or NULL on error.
  31.  */
  32.  
  33. /*
  34.  * The following hokiness with global variables is so that on
  35.  * receipt of an interrupt signal, the partial message can be salted
  36.  * away on dead.letter.
  37.  */
  38.  
  39. static    int    (*saveint)();        /* Previous SIGINT value */
  40. static    int    (*savehup)();        /* Previous SIGHUP value */
  41. static    int    (*savecont)();        /* Previous SIGCONT value */
  42. static    FILE    *collf;            /* File for saving away */
  43. static    int    hadintr;        /* Have seen one SIGINT so far */
  44.  
  45. static    jmp_buf    coljmp;            /* To get back to work */
  46.  
  47. FILE *
  48. collect(hp)
  49.     struct header *hp;
  50. {
  51.     FILE *fp, *fbuf;
  52.     int lc, cc, escape, eof;
  53.     int collrub(), intack(), collcont();
  54.     register int c, t;
  55.     char linebuf[LINESIZE], *cp;
  56.     extern char tempMail[];
  57.     int notify();
  58.     char getsub;
  59.     int omask;
  60.  
  61.     noreset++;
  62.     fp = NULL;
  63.     collf = NULL;
  64.  
  65.     /*
  66.      * Start catching signals from here, but we're still die on interrupts
  67.      * until we're in the main loop.
  68.      */
  69.     omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
  70.     if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
  71.         signal(SIGINT, value("ignore") != NOSTR ? intack : collrub);
  72.     if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
  73.         signal(SIGHUP, collrub);
  74.     savecont = signal(SIGCONT, SIG_DFL);
  75.     if (setjmp(coljmp)) {
  76.         remove(tempMail);
  77.         goto err;
  78.     }
  79.     sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
  80.  
  81.     if ((fp = fopen(tempMail, "w+")) == NULL) {
  82.         perror(tempMail);
  83.         goto err;
  84.     }
  85.     collf = fp;
  86.     remove(tempMail);
  87.  
  88.     /*
  89.      * If we are going to prompt for a subject,
  90.      * refrain from printing a newline after
  91.      * the headers (since some people mind).
  92.      */
  93.     t = GTO|GSUBJECT|GCC|GNL;
  94.     getsub = 0;
  95.     if (intty && sflag == NOSTR && hp->h_subject == NOSTR && value("ask"))
  96.         t &= ~GNL, getsub++;
  97.     if (hp->h_seq != 0) {
  98.         puthead(hp, stdout, t);
  99.         fflush(stdout);
  100.     }
  101.     escape = ESCAPE;
  102.     if ((cp = value("escape")) != NOSTR)
  103.         escape = *cp;
  104.     eof = 0;
  105.     hadintr = 0;
  106.  
  107.     /*
  108.      * We can put the setjmp here because register variable
  109.      * needs to be saved in the loop.
  110.      */
  111.     if (!setjmp(coljmp)) {
  112.         signal(SIGCONT, collcont);
  113.         if (getsub)
  114.             grabh(hp, GSUBJECT);
  115.     } else {
  116.         /*
  117.          * Come here for printing the after-signal message.
  118.          * Duplicate messages won't be printed because
  119.          * the write is aborted if we get a SIGTTOU.
  120.          */
  121. cont:
  122.         if (hadintr) {
  123.             fflush(stdout);
  124.             fprintf(stderr,
  125.             "\n(Interrupt -- one more to kill letter)\n");
  126.         } else {
  127.             printf("(continue)\n");
  128.             fflush(stdout);
  129.         }
  130.     }
  131.     for (;;) {
  132.         if (readline(stdin, linebuf) < 0) {
  133.             if (intty && value("ignoreeof") != NOSTR) {
  134.                 if (++eof > 35)
  135.                     break;
  136.                 printf("Use \".\" to terminate letter\n");
  137.                 continue;
  138.             }
  139.             break;
  140.         }
  141.         eof = 0;
  142.         hadintr = 0;
  143.         if (intty && equal(".", linebuf) &&
  144.             (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
  145.             break;
  146.         if (linebuf[0] != escape || rflag != NOSTR) {
  147.             if (putline(fp, linebuf) < 0)
  148.                 goto err;
  149.             continue;
  150.         }
  151.         c = linebuf[1];
  152.         switch (c) {
  153.         default:
  154.             /*
  155.              * On double escape, just send the single one.
  156.              * Otherwise, it's an error.
  157.              */
  158.  
  159.             if (c == escape) {
  160.                 if (putline(fp, &linebuf[1]) < 0)
  161.                     goto err;
  162.                 else
  163.                     break;
  164.             }
  165.             printf("Unknown tilde escape.\n");
  166.             break;
  167.  
  168.         case 'C':
  169.             /*
  170.              * Dump core.
  171.              */
  172.  
  173.             core();
  174.             break;
  175.  
  176.         case '!':
  177.             /*
  178.              * Shell escape, send the balance of the
  179.              * line to sh -c.
  180.              */
  181.  
  182.             shell(&linebuf[2]);
  183.             break;
  184.  
  185.         case ':':
  186.         case '_':
  187.             /*
  188.              * Escape to command mode, but be nice!
  189.              */
  190.  
  191.             execute(&linebuf[2], 1);
  192.             goto cont;
  193.  
  194.         case '.':
  195.             /*
  196.              * Simulate end of file on input.
  197.              */
  198.             goto out;
  199.  
  200.         case 'q':
  201.         case 'Q':
  202.             /*
  203.              * Force a quit of sending mail.
  204.              * Act like an interrupt happened.
  205.              */
  206.  
  207.             hadintr++;
  208.             collrub(SIGINT);
  209.             exit(1);
  210.  
  211.         case 'h':
  212.             /*
  213.              * Grab a bunch of headers.
  214.              */
  215.             if (!intty || !outtty) {
  216.                 printf("~h: no can do!?\n");
  217.                 break;
  218.             }
  219.             grabh(hp, GTO|GSUBJECT|GCC|GBCC);
  220.             goto cont;
  221.  
  222.         case 't':
  223.             /*
  224.              * Add to the To list.
  225.              */
  226.  
  227.             hp->h_to = addto(hp->h_to, &linebuf[2]);
  228.             hp->h_seq++;
  229.             break;
  230.  
  231.         case 's':
  232.             /*
  233.              * Set the Subject list.
  234.              */
  235.  
  236.             cp = &linebuf[2];
  237.             while (isspace(*cp))
  238.                 cp++;
  239.             hp->h_subject = savestr(cp);
  240.             hp->h_seq++;
  241.             break;
  242.  
  243.         case 'c':
  244.             /*
  245.              * Add to the CC list.
  246.              */
  247.  
  248.             hp->h_cc = addto(hp->h_cc, &linebuf[2]);
  249.             hp->h_seq++;
  250.             break;
  251.  
  252.         case 'b':
  253.             /*
  254.              * Add stuff to blind carbon copies list.
  255.              */
  256.             hp->h_bcc = addto(hp->h_bcc, &linebuf[2]);
  257.             hp->h_seq++;
  258.             break;
  259.  
  260.         case 'd':
  261.             strcpy(linebuf + 2, deadletter);
  262.             /* fall into . . . */
  263.  
  264.         case 'r':
  265.             /*
  266.              * Invoke a file:
  267.              * Search for the file name,
  268.              * then open it and copy the contents to fp.
  269.              */
  270.  
  271.             cp = &linebuf[2];
  272.             while (isspace(*cp))
  273.                 cp++;
  274.             if (*cp == '\0') {
  275.                 printf("Interpolate what file?\n");
  276.                 break;
  277.             }
  278.             cp = expand(cp);
  279.             if (cp == NOSTR)
  280.                 break;
  281.             if (isdir(cp)) {
  282.                 printf("%s: Directory\n", cp);
  283.                 break;
  284.             }
  285.             if ((fbuf = fopen(cp, "r")) == NULL) {
  286.                 perror(cp);
  287.                 break;
  288.             }
  289.             printf("\"%s\" ", cp);
  290.             fflush(stdout);
  291.             lc = 0;
  292.             cc = 0;
  293.             while (readline(fbuf, linebuf) >= 0) {
  294.                 lc++;
  295.                 if ((t = putline(fp, linebuf)) < 0) {
  296.                     fclose(fbuf);
  297.                     goto err;
  298.                 }
  299.                 cc += t;
  300.             }
  301.             fclose(fbuf);
  302.             printf("%d/%d\n", lc, cc);
  303.             break;
  304.  
  305.         case 'w':
  306.             /*
  307.              * Write the message on a file.
  308.              */
  309.  
  310.             cp = &linebuf[2];
  311.             while (any(*cp, " \t"))
  312.                 cp++;
  313.             if (*cp == '\0') {
  314.                 fprintf(stderr, "Write what file!?\n");
  315.                 break;
  316.             }
  317.             if ((cp = expand(cp)) == NOSTR)
  318.                 break;
  319.             rewind(fp);
  320.             exwrite(cp, fp, 1);
  321.             break;
  322.  
  323.         case 'm':
  324.         case 'f':
  325.             /*
  326.              * Interpolate the named messages, if we
  327.              * are in receiving mail mode.  Does the
  328.              * standard list processing garbage.
  329.              * If ~f is given, we don't shift over.
  330.              */
  331.  
  332.             if (!rcvmode) {
  333.                 printf("No messages to send from!?!\n");
  334.                 break;
  335.             }
  336.             cp = &linebuf[2];
  337.             while (any(*cp, " \t"))
  338.                 cp++;
  339.             if (forward(cp, fp, c) < 0)
  340.                 goto err;
  341.             goto cont;
  342.  
  343.         case '?':
  344.             if ((fbuf = fopen(THELPFILE, "r")) == NULL) {
  345.                 perror(THELPFILE);
  346.                 break;
  347.             }
  348.             while ((t = getc(fbuf)) != EOF)
  349.                 putchar(t);
  350.             fclose(fbuf);
  351.             break;
  352.  
  353.         case 'p':
  354.             /*
  355.              * Print out the current state of the
  356.              * message without altering anything.
  357.              */
  358.  
  359.             rewind(fp);
  360.             printf("-------\nMessage contains:\n");
  361.             puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
  362.             while ((t = getc(fp)) != EOF)
  363.                 putchar(t);
  364.             goto cont;
  365.  
  366.         case '^':
  367.         case '|':
  368.             /*
  369.              * Pipe message through command.
  370.              * Collect output as new message.
  371.              */
  372.  
  373.             rewind(fp);
  374.             fp = mespipe(fp, &linebuf[2]);
  375.             goto cont;
  376.  
  377.         case 'v':
  378.         case 'e':
  379.             /*
  380.              * Edit the current message.
  381.              * 'e' means to use EDITOR
  382.              * 'v' means to use VISUAL
  383.              */
  384.  
  385.             rewind(fp);
  386.             if ((fp = mesedit(fp, c)) == NULL)
  387.                 goto err;
  388.             goto cont;
  389.         }
  390.     }
  391.     goto out;
  392. err:
  393.     if (fp != NULL) {
  394.         fclose(fp);
  395.         fp = NULL;
  396.     }
  397. out:
  398.     if (fp != NULL)
  399.         rewind(fp);
  400.     signal(SIGINT, saveint);
  401.     signal(SIGHUP, savehup);
  402.     signal(SIGCONT, savecont);
  403.     sigsetmask(omask);
  404.     noreset = 0;
  405.     return(fp);
  406. }
  407.  
  408. /*
  409.  * Write a file, ex-like if f set.
  410.  */
  411.  
  412. exwrite(name, fp, f)
  413.     char name[];
  414.     FILE *fp;
  415. {
  416.     register FILE *of;
  417.     register int c;
  418.     long cc;
  419.     int lc;
  420.     struct stat junk;
  421.  
  422.     if (f) {
  423.         printf("\"%s\" ", name);
  424.         fflush(stdout);
  425.     }
  426.     if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
  427.         if (!f)
  428.             fprintf(stderr, "%s: ", name);
  429.         fprintf(stderr, "File exists\n");
  430.         return(-1);
  431.     }
  432.     if ((of = fopen(name, "w")) == NULL) {
  433.         perror(NOSTR);
  434.         return(-1);
  435.     }
  436.     lc = 0;
  437.     cc = 0;
  438.     while ((c = getc(fp)) != EOF) {
  439.         cc++;
  440.         if (c == '\n')
  441.             lc++;
  442.         putc(c, of);
  443.         if (ferror(of)) {
  444.             perror(name);
  445.             fclose(of);
  446.             return(-1);
  447.         }
  448.     }
  449.     fclose(of);
  450.     printf("%d/%ld\n", lc, cc);
  451.     fflush(stdout);
  452.     return(0);
  453. }
  454.  
  455. /*
  456.  * Edit the message being collected on fp.
  457.  * Write the message out onto some poorly-named temp file
  458.  * and point an editor at it.
  459.  *
  460.  * On return, make the edit file the new temp file.
  461.  */
  462.  
  463. FILE *
  464. mesedit(fp, c)
  465.     FILE *fp;
  466. {
  467.     int pid;
  468.     union wait s;
  469.     FILE *fbuf;
  470.     register int t;
  471.     int (*sigint)(), (*sigcont)();
  472.     struct stat sbuf;
  473.     extern char tempEdit[];
  474.     register char *edit;
  475.  
  476.     sigint = signal(SIGINT, SIG_IGN);
  477.     sigcont = signal(SIGCONT, SIG_DFL);
  478.     if (stat(tempEdit, &sbuf) >= 0) {
  479.         printf("%s: file exists\n", tempEdit);
  480.         goto out;
  481.     }
  482.     close(creat(tempEdit, 0600));
  483.     if ((fbuf = fopen(tempEdit, "w")) == NULL) {
  484.         perror(tempEdit);
  485.         goto out;
  486.     }
  487.     while ((t = getc(fp)) != EOF)
  488.         putc(t, fbuf);
  489.     fflush(fbuf);
  490.     if (ferror(fbuf)) {
  491.         perror(tempEdit);
  492.         remove(tempEdit);
  493.         goto fix;
  494.     }
  495.     fclose(fbuf);
  496.     if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR)
  497.         edit = c == 'e' ? EDITOR : VISUAL;
  498.     pid = vfork();
  499.     if (pid == 0) {
  500.         if (sigint != SIG_IGN)
  501.             signal(SIGINT, SIG_DFL);
  502.         execl(edit, edit, tempEdit, 0);
  503.         perror(edit);
  504.         _exit(1);
  505.     }
  506.     if (pid == -1) {
  507.         perror("fork");
  508.         remove(tempEdit);
  509.         goto out;
  510.     }
  511.     while (wait(&s) != pid)
  512.         ;
  513.     if (s.w_status != 0) {
  514.         printf("Fatal error in \"%s\"\n", edit);
  515.         remove(tempEdit);
  516.         goto out;
  517.     }
  518.  
  519.     /*
  520.      * Now switch to new file.
  521.      */
  522.  
  523.     if ((fbuf = fopen(tempEdit, "a+")) == NULL) {
  524.         perror(tempEdit);
  525.         remove(tempEdit);
  526.         goto out;
  527.     }
  528.     remove(tempEdit);
  529.     collf = fbuf;
  530.     fclose(fp);
  531.     fp = fbuf;
  532.     goto out;
  533. fix:
  534.     perror(tempEdit);
  535. out:
  536.     signal(SIGCONT, sigcont);
  537.     signal(SIGINT, sigint);
  538.     return(fp);
  539. }
  540.  
  541. /*
  542.  * Pipe the message through the command.
  543.  * Old message is on stdin of command;
  544.  * New message collected from stdout.
  545.  * Sh -c must return 0 to accept the new message.
  546.  */
  547.  
  548. FILE *
  549. mespipe(fp, cmd)
  550.     FILE *fp;
  551.     char cmd[];
  552. {
  553.     extern char tempEdit[];
  554.     register FILE *nf;
  555.     int pid;
  556.     union wait s;
  557.     int (*saveint)();
  558.     char *Shell;
  559.  
  560.     if ((nf = fopen(tempEdit, "w+")) == NULL) {
  561.         perror(tempEdit);
  562.         return(fp);
  563.     }
  564.     remove(tempEdit);
  565.     saveint = signal(SIGINT, SIG_IGN);
  566.     if ((Shell = value("SHELL")) == NULL)
  567.         Shell = "/bin/sh";
  568.     if ((pid = vfork()) == -1) {
  569.         perror("fork");
  570.         goto err;
  571.     }
  572.     if (pid == 0) {
  573.         int fd;
  574.         /*
  575.          * stdin = current message.
  576.          * stdout = new message.
  577.          */
  578.  
  579.         close(0);
  580.         dup(fileno(fp));
  581.         close(1);
  582.         dup(fileno(nf));
  583.         for (fd = getdtablesize(); --fd > 2;)
  584.             close(fd);
  585.         execl(Shell, Shell, "-c", cmd, 0);
  586.         perror(Shell);
  587.         _exit(1);
  588.     }
  589.     while (wait(&s) != pid)
  590.         ;
  591.     if (s.w_status != 0 || pid == -1) {
  592.         fprintf(stderr, "\"%s\" failed!?\n", cmd);
  593.         goto err;
  594.     }
  595.     if (fsize(nf) == 0) {
  596.         fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
  597.         goto err;
  598.     }
  599.  
  600.     /*
  601.      * Take new files.
  602.      */
  603.  
  604.     fseek(nf, 0L, 2);
  605.     collf = nf;
  606.     fclose(fp);
  607.     signal(SIGINT, saveint);
  608.     return(nf);
  609.  
  610. err:
  611.     fclose(nf);
  612.     signal(SIGINT, saveint);
  613.     return(fp);
  614. }
  615.  
  616. /*
  617.  * Interpolate the named messages into the current
  618.  * message, preceding each line with a tab.
  619.  * Return a count of the number of characters now in
  620.  * the message, or -1 if an error is encountered writing
  621.  * the message temporary.  The flag argument is 'm' if we
  622.  * should shift over and 'f' if not.
  623.  */
  624. forward(ms, fp, f)
  625.     char ms[];
  626.     FILE *fp;
  627. {
  628.     register int *msgvec, *ip;
  629.     extern char tempMail[];
  630.  
  631.     msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
  632.     if (msgvec == (int *) NOSTR)
  633.         return(0);
  634.     if (getmsglist(ms, msgvec, 0) < 0)
  635.         return(0);
  636.     if (*msgvec == NULL) {
  637.         *msgvec = first(0, MMNORM);
  638.         if (*msgvec == NULL) {
  639.             printf("No appropriate messages\n");
  640.             return(0);
  641.         }
  642.         msgvec[1] = NULL;
  643.     }
  644.     printf("Interpolating:");
  645.     for (ip = msgvec; *ip != NULL; ip++) {
  646.         touch(*ip);
  647.         printf(" %d", *ip);
  648.         if (f == 'm') {
  649.             if (transmit(&message[*ip-1], fp) < 0L) {
  650.                 perror(tempMail);
  651.                 return(-1);
  652.             }
  653.         } else
  654.             if (send(&message[*ip-1], fp, 0) < 0) {
  655.                 perror(tempMail);
  656.                 return(-1);
  657.             }
  658.     }
  659.     printf("\n");
  660.     return(0);
  661. }
  662.  
  663. /*
  664.  * Send message described by the passed pointer to the
  665.  * passed output buffer.  Insert a tab in front of each
  666.  * line.  Return a count of the characters sent, or -1
  667.  * on error.
  668.  */
  669.  
  670. long
  671. transmit(mailp, fp)
  672.     struct message *mailp;
  673.     FILE *fp;
  674. {
  675.     register struct message *mp;
  676.     register int ch;
  677.     long c, n;
  678.     int bol;
  679.     FILE *ibuf;
  680.  
  681.     mp = mailp;
  682.     ibuf = setinput(mp);
  683.     c = mp->m_size;
  684.     n = c;
  685.     bol = 1;
  686.     while (c-- > 0L) {
  687.         ch = getc(ibuf);
  688.         if (ch == '\n')
  689.             bol = 1;
  690.         else if (bol) {
  691.             bol = 0;
  692.             putc('\t', fp);
  693.             n++;
  694.         }
  695.         putc(ch, fp);
  696.         if (ferror(fp)) {
  697.             perror("/tmp");
  698.             return(-1L);
  699.         }
  700.     }
  701.     return(n);
  702. }
  703.  
  704. /*
  705.  * Print (continue) when continued after ^Z.
  706.  */
  707. /*ARGSUSED*/
  708. collcont(s)
  709. {
  710.  
  711.     hadintr = 0;
  712.     longjmp(coljmp, 1);
  713. }
  714.  
  715. /*
  716.  * On interrupt, go here to save the partial
  717.  * message on ~/dead.letter.
  718.  * Then restore signals and execute the normal
  719.  * signal routine.  We only come here if signals
  720.  * were previously set anyway.
  721.  */
  722.  
  723. collrub(s)
  724. {
  725.     register FILE *dbuf;
  726.     register int c;
  727.  
  728.     if (s == SIGINT && hadintr == 0) {
  729.         hadintr = 1;
  730.         longjmp(coljmp, 1);
  731.     }
  732.     rewind(collf);
  733.     if (s == SIGINT && value("nosave") != NOSTR || fsize(collf) == 0)
  734.         goto done;
  735.     if ((dbuf = fopen(deadletter, "w")) == NULL)
  736.         goto done;
  737.     chmod(deadletter, 0600);
  738.     while ((c = getc(collf)) != EOF)
  739.         putc(c, dbuf);
  740.     fclose(dbuf);
  741.  
  742. done:
  743.     fclose(collf);
  744.     signal(SIGINT, saveint);
  745.     signal(SIGHUP, savehup);
  746.     signal(SIGCONT, savecont);
  747.     if (rcvmode) {
  748.         if (s == SIGHUP)
  749.             hangup(SIGHUP);
  750.         else
  751.             stop(s);
  752.     } else
  753.         exit(1);
  754. }
  755.  
  756. /*
  757.  * Acknowledge an interrupt signal from the tty by typing an @
  758.  */
  759.  
  760. /*ARGSUSED*/
  761. intack(s)
  762. {
  763.  
  764.     puts("@");
  765.     fflush(stdout);
  766.     clearerr(stdin);
  767. }
  768.  
  769. /*
  770.  * Add a string to the end of a header entry field.
  771.  */
  772.  
  773. char *
  774. addto(hf, news)
  775.     char hf[], news[];
  776. {
  777.     register char *cp, *cp2, *linebuf;
  778.  
  779.     if (hf == NOSTR)
  780.         hf = "";
  781.     if (*news == '\0')
  782.         return(hf);
  783.     linebuf = salloc(strlen(hf) + strlen(news) + 2);
  784.     for (cp = hf; any(*cp, " \t"); cp++)
  785.         ;
  786.     for (cp2 = linebuf; *cp;)
  787.         *cp2++ = *cp++;
  788.     *cp2++ = ' ';
  789.     for (cp = news; any(*cp, " \t"); cp++)
  790.         ;
  791.     while (*cp != '\0')
  792.         *cp2++ = *cp++;
  793.     *cp2 = '\0';
  794.     return(linebuf);
  795. }
  796.